﻿Imports Aricie.Web.UI
Imports Aricie.DNN.Modules.LuceneSearch.Business
Imports Aricie.DNN.Modules.LuceneSearch.UI.Controls

Imports DotNetNuke.Services.Exceptions
Imports DotNetNuke.UI.UserControls
Imports DotNetNuke.UI.Utilities

Imports Lucene.Net.Index
Imports Lucene.Net.Search
Imports DotNetNuke.Services.Localization
Imports Aricie.DNN.Modules.LuceneSearch.Settings

Namespace Aricie.DNN.Modules.LuceneSearch.UI


    Partial Public Class AdvancedCriteriaFilter
        Inherits LuceneFilterControlBase

#Region "Public Properties"
     

        Public Property ShowPhraseQuery() As Boolean
            Get
                If Me.ViewState("ShowPhraseQuery") Is Nothing Then
                    Me.ViewState("ShowPhraseQuery") = True
                End If
                Return CBool(Me.ViewState("ShowPhraseQuery"))
            End Get
            Set(ByVal value As Boolean)
                Me.ViewState("ShowPhraseQuery") = value
            End Set
        End Property

        Public Property ShowBooleanShould() As Boolean
            Get
                If Me.ViewState("ShowBooleanShould") Is Nothing Then
                    Me.ViewState("ShowBooleanShould") = True
                End If
                Return CBool(Me.ViewState("ShowBooleanShould"))
            End Get
            Set(ByVal value As Boolean)
                Me.ViewState("ShowBooleanShould") = value
            End Set
        End Property

        Public Property ShowBooleanMust() As Boolean
            Get
                If Me.ViewState("ShowBooleanMust") Is Nothing Then
                    Me.ViewState("ShowBooleanMust") = True
                End If
                Return CBool(Me.ViewState("ShowBooleanMust"))
            End Get
            Set(ByVal value As Boolean)
                Me.ViewState("ShowBooleanMust") = value
            End Set
        End Property

        Public Property DefaultFuzzyValue() As Double
            Get
                If Me.ViewState("DefaultFuzzyValue") Is Nothing Then
                    Me.ViewState("DefaultFuzzyValue") = 1
                End If
                Return CDbl(Me.ViewState("DefaultFuzzyValue"))
            End Get
            Set(ByVal value As Double)
                Me.ViewState("DefaultFuzzyValue") = value
            End Set
        End Property

        Public Property DefaultSlopValue() As Integer
            Get
                If Me.ViewState("DefaultSlopValue") Is Nothing Then
                    Me.ViewState("DefaultSlopValue") = 0
                End If
                Return CInt(Me.ViewState("DefaultSlopValue"))
            End Get
            Set(ByVal value As Integer)
                Me.ViewState("DefaultSlopValue") = value
            End Set
        End Property

        Public Property EnableCustomFieldCriteria() As Boolean
            Get
                Return Me.grdSearchCriteria.Columns(0).Visible
            End Get
            Set(ByVal value As Boolean)
                Me.grdSearchCriteria.Columns(0).Visible = value
            End Set
        End Property

        Public Property EnableCustomOccurCriteria() As Boolean
            Get
                Return Me.grdSearchCriteria.Columns(1).Visible
            End Get
            Set(ByVal value As Boolean)
                Me.grdSearchCriteria.Columns(1).Visible = value
            End Set
        End Property

        Public Property EnableCustomFuzzyCriteria() As Boolean
            Get
                Return Me.grdSearchCriteria.Columns(4).Visible
            End Get
            Set(ByVal value As Boolean)
                Me.grdSearchCriteria.Columns(4).Visible = value
            End Set
        End Property

        Public Property EnableFreeLuceneQuery() As Boolean
            Get
                If Me.ViewState("EnableFreeLuceneQuery") Is Nothing Then
                    Me.ViewState("EnableFreeLuceneQuery") = True
                End If
                Return CBool(Me.ViewState("EnableFreeLuceneQuery"))
            End Get
            Set(ByVal value As Boolean)
                Me.ViewState("EnableFreeLuceneQuery") = value
            End Set
        End Property

        Public Overrides Property Enabled() As Boolean
            Get
                Return Me.grdSearchCriteria.Enabled
            End Get
            Set(ByVal value As Boolean)
                Me.grdSearchCriteria.Enabled = value
            End Set
        End Property
#End Region

#Region "Event Handler"
        Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
            If Not Page.IsPostBack Then
                Me.InitLayout()
                BindData()
            End If

        End Sub

        Protected Sub grdSearchCriteria_RowDataBound(ByVal sender As Object, ByVal e As GridViewRowEventArgs) _
           Handles grdSearchCriteria.RowDataBound
            Try
                Select Case e.Row.RowType
                    Case DataControlRowType.DataRow
                        Dim ctlOccur As OccurSelector = DirectCast(e.Row.FindControl("ctlOccur"), OccurSelector)
                        Dim _
                            ctlCriteriaType As HiddenField = _
                                DirectCast(e.Row.FindControl("ctlCriteriaType"), HiddenField)
                        Dim txtSearchCriteria As TextBox = DirectCast(e.Row.FindControl("txtSearchCriteria"), TextBox)
                        Dim _
                            ctlSearchFieldSelector As SearchFieldSelector = _
                                DirectCast(e.Row.FindControl("ctlSearchFieldSelector"), SearchFieldSelector)
                        Dim _
                            ctlFuzzyFilter As FuzzyFilter = _
                                DirectCast(e.Row.FindControl("ctlFuzzyFilter"), FuzzyFilter)
                        Dim ctlSlopFilter As SlopFilter = DirectCast(e.Row.FindControl("ctlSlopFilter"), SlopFilter)
                        Dim _
                            plCriteriaType As LabelControl = _
                                DirectCast(e.Row.FindControl("plCriteriaType"), LabelControl)

                        Dim asciiEnter As Integer = Asc(vbCr)
                        Dim objLink As LinkButton = FindControlsRecursive(Of LinkButton)(Me.Parent.FindControl("pnlCmdSearch"))(1)
                        ClientAPI.RegisterKeyCapture(ctlOccur, objLink, asciiEnter)
                        ClientAPI.RegisterKeyCapture(ctlCriteriaType, objLink, asciiEnter)
                        ClientAPI.RegisterKeyCapture(ctlSearchFieldSelector, objLink, asciiEnter)
                        ClientAPI.RegisterKeyCapture(ctlFuzzyFilter, objLink, asciiEnter)
                        ClientAPI.RegisterKeyCapture(txtSearchCriteria, objLink, asciiEnter)

                        ctlFuzzyFilter.FuzzyRatio = Me.DefaultFuzzyValue
                        ctlSlopFilter.SlopRatio = Me.DefaultSlopValue


                        Dim _
                            data As KeyValuePair(Of CriteriaType, BooleanClause) = _
                                DirectCast(e.Row.DataItem, KeyValuePair(Of CriteriaType, BooleanClause))

                        ctlCriteriaType.Value = data.Key.ToString

                        Dim rowClause As BooleanClause = data.Value
                        ctlOccur.SelectedObject = rowClause.Occur


                        Dim objQuery As Query = rowClause.Query
                        Dim criteriaBuilder As New StringBuilder
                        Select Case data.Key
                            Case CriteriaType.PhraseQuery
                                ctlFuzzyFilter.Visible = False
                                ctlSlopFilter.Visible = True
                                Dim objTerms As Term() = DirectCast(objQuery, PhraseQuery).GetTerms
                                If objTerms.Length > 0 Then
                                    Dim fieldId As String = objTerms(0).Field
                                    If Not String.IsNullOrEmpty(fieldId) Then
                                        ctlSearchFieldSelector.SelectedObject = _
                                            FieldFactory.Instance.GetDefinition(fieldId)
                                    End If
                                    For Each objTerm As Term In objTerms
                                        criteriaBuilder.AppendFormat("{0} ", BusinessController.GetTermText(objTerm))
                                    Next

                                End If
                            Case CriteriaType.BooleanMust, CriteriaType.BooleanShould
                                ctlFuzzyFilter.Visible = True
                                ctlSlopFilter.Visible = False
                                Dim objClauses As BooleanClause() = DirectCast(objQuery, BooleanQuery).GetClauses

                                If objClauses.Length > 0 Then
                                    'Dim clause As BooleanClause = objClauses(0)

                                    Dim objTermQuery As Query = objClauses(0).Query()
                                    Dim fieldId As String = ""
                                    If TypeOf objTermQuery Is FuzzyQuery Then
                                        fieldId = BusinessController.ExtractAllTermsGetFirst(objTermQuery).Field
                                        ctlFuzzyFilter.FuzzyRatio = _
                                            DirectCast(objTermQuery, FuzzyQuery).MinSimilarity
                                    Else
                                        fieldId = DirectCast(objTermQuery, TermQuery).Term.Field
                                    End If
                                    If Not String.IsNullOrEmpty(fieldId) Then
                                        ctlSearchFieldSelector.SelectedObject = _
                                            FieldFactory.Instance.GetDefinition(fieldId)
                                    End If
                                    Dim objTerm As Term
                                    For Each clause As BooleanClause In objClauses
                                        objTerm = BusinessController.ExtractAllTermsGetFirst(clause.Query())
                                        criteriaBuilder.AppendFormat("{0} ", BusinessController.GetTermText(objTerm))
                                    Next
                                End If
                            Case Else
                                ctlFuzzyFilter.Visible = False
                                ctlSlopFilter.Visible = False
                                If objQuery IsNot Nothing Then
                                    criteriaBuilder.Append(objQuery.ToString)
                                End If
                                txtSearchCriteria.TextMode = TextBoxMode.MultiLine
                                txtSearchCriteria.Rows = 3


                        End Select
                        txtSearchCriteria.Text = criteriaBuilder.ToString().Trim
                        plCriteriaType.ResourceKey = ctlCriteriaType.Value
                End Select
            Catch ex As Exception
                ProcessModuleLoadException(Me, ex)
            End Try

        End Sub

        Protected Sub grdSearchCriteria_Load(ByVal sender As Object, ByVal e As EventArgs) _
         Handles grdSearchCriteria.Load
            Try
                If Me.IsPostBack Then
                    For Each objRow As GridViewRow In Me.grdSearchCriteria.Rows
                        If objRow.RowType = DataControlRowType.DataRow Then
                            Dim _
                                plCriteriaType As LabelControl = _
                                    DirectCast(objRow.FindControl("plCriteriaType"), LabelControl)
                            Dim _
                                ctlCriteriaType As HiddenField = _
                                    DirectCast(objRow.FindControl("ctlCriteriaType"), HiddenField)
                            plCriteriaType.ResourceKey = ctlCriteriaType.Value
                        End If
                    Next
                End If

            Catch ex As Exception
                ProcessModuleLoadException(Me, ex)
            End Try
        End Sub
#End Region

#Region "LuceneControlBase Methods"

        Public Overrides Function GetFieldNames() As System.Collections.Generic.IEnumerable(Of String)
            Return New String() {}
        End Function

        Public Overrides Function GetFilter() As LuceneFilterQuery
            Dim toReturn As New LuceneFilterQuery

            toReturn.SearchQuery = GetCurrentSearchQuery().ToString
            Return toReturn
        End Function

        Public Overrides Function HasFilter() As Boolean
            Return GetCurrentSearchQuery() IsNot Nothing
        End Function

        Public Overrides Function ParseFilter(ByVal objFilter As LuceneFilterQuery) As Boolean
            Dim searchQuery As Query = BusinessController.GetQuery(Me.PortalId, objFilter.SearchQuery)
            Dim searchDataSource As Dictionary(Of CriteriaType, BooleanClause) = BuildSearchDataSource(searchQuery)
            Me.grdSearchCriteria.DataSource = searchDataSource
            Me.grdSearchCriteria.DataBind()
        End Function

#End Region

#Region "Private Methods"
        Private Sub BindData()
            Dim searchQuery As Query
            If Not String.IsNullOrEmpty(BusinessController.HttpCriteriaSring) Then
                searchQuery = BusinessController.GetQuery(Me.PortalId, BusinessController.HttpCriteriaSring)
            Else
                searchQuery = Nothing
            End If
            Dim searchDataSource As Dictionary(Of CriteriaType, BooleanClause) = BuildSearchDataSource(searchQuery)
            Me.grdSearchCriteria.DataSource = searchDataSource
            Me.grdSearchCriteria.DataBind()
        End Sub


        'Private Sub Localize()

        '    Localization.LocalizeGridView(DirectCast(Me.grdSearchCriteria, GridView), Me.LocalResourceFile)

        'End Sub

        Private Sub InitLayout()
            Me.DefaultFuzzyValue = CDbl(((CurrentSettings.DefaultFuzzyValue) / 100))
            Me.DefaultSlopValue = CurrentSettings.DefaultSlopValue

            Me.ShowPhraseQuery = CurrentSettings.ShowPhraseQuery
            Me.ShowBooleanShould = CurrentSettings.ShowBooleanShould
            Me.ShowBooleanMust = CurrentSettings.ShowBooleanMust

            Me.EnableCustomFieldCriteria = CurrentSettings.EnableCustomFieldCriteria
            Me.EnableCustomFuzzyCriteria = CurrentSettings.EnableCustomFuzzyCriteria
            Me.EnableCustomOccurCriteria = CurrentSettings.EnableCustomOccurCriteria
            Me.EnableFreeLuceneQuery = CurrentSettings.EnableFreeLuceneQuery

        End Sub


        Private Function BuildSearchDataSource(ByVal objQuery As Query) As Dictionary(Of CriteriaType, BooleanClause)

            Dim defaultDico As Dictionary(Of CriteriaType, BooleanClause) = GetDefaultDataSource()
            Dim toReturn As New Dictionary(Of CriteriaType, BooleanClause)

            Dim nbFree As Integer = 0
            If objQuery IsNot Nothing Then
                If Not TypeOf objQuery Is BooleanQuery Then
                    Select Case objQuery.GetType.Name
                        Case GetType(PhraseQuery).Name
                            toReturn(CriteriaType.PhraseQuery) = New BooleanClause(objQuery, Occur.MUST)
                        Case GetType(TermQuery).Name
                            Dim _
                                terms As String() = _
                                    BusinessController.GetTermText(DirectCast(objQuery, TermQuery).Term).Split(" "c)
                            Dim bQuery As New BooleanQuery()
                            For Each strTerm As String In terms
                                bQuery.Add(New TermQuery(New Term("", strTerm)), Occur.SHOULD)
                            Next
                            toReturn(CriteriaType.BooleanShould) = New BooleanClause(bQuery, Occur.MUST)
                        Case Else
                            toReturn(CriteriaType.FreeQuery) = New BooleanClause(objQuery, Occur.MUST)
                    End Select
                Else
                    For Each clause As BooleanClause In DirectCast(objQuery, BooleanQuery).GetClauses
                        Dim subQuery As Query = clause.Query
                        Select Case subQuery.GetType.Name
                            Case GetType(PhraseQuery).Name
                                toReturn(CriteriaType.PhraseQuery) = clause
                            Case GetType(BooleanQuery).Name
                                Dim targetCriteriaType As CriteriaType = CriteriaType.BooleanShould
                                Dim subClauses As BooleanClause() = DirectCast(subQuery, BooleanQuery).GetClauses
                                For Each subclause As BooleanClause In subClauses
                                    If _
                                        (Not TypeOf subclause.Query Is FuzzyQuery) AndAlso _
                                        (Not TypeOf subclause.Query Is TermQuery) Then
                                        targetCriteriaType = CriteriaType.FreeQuery
                                        nbFree += 1
                                        Exit For
                                    End If
                                    If subclause.Occur.Equals(Occur.MUST) Then
                                        targetCriteriaType = CriteriaType.BooleanMust
                                    End If
                                Next
                                toReturn(targetCriteriaType) = clause
                            Case Else
                                toReturn(CriteriaType.FreeQuery) = clause
                                nbFree += 1
                        End Select
                    Next
                    If nbFree > 1 Then
                        'plus d'une requête free -> l'ensemble est une requête free
                        toReturn.Clear()
                        toReturn(CriteriaType.FreeQuery) = New BooleanClause(objQuery, Occur.SHOULD)

                    End If
                End If
            End If
            For Each key As CriteriaType In defaultDico.Keys
                If Not toReturn.ContainsKey(key) Then
                    toReturn(key) = defaultDico(key)
                End If
            Next


            Return toReturn


        End Function


        Private Function GetDefaultDataSource() As Dictionary(Of CriteriaType, BooleanClause)
            Dim toReturn As New Dictionary(Of CriteriaType, BooleanClause)

            If Me.ShowPhraseQuery Then
                toReturn.Add(CriteriaType.PhraseQuery, New BooleanClause(New PhraseQuery, Occur.MUST))
            End If

            If Me.ShowBooleanShould Then
                toReturn.Add(CriteriaType.BooleanShould, New BooleanClause(New BooleanQuery, Occur.MUST))
            End If


            If Me.ShowBooleanMust Then
                toReturn.Add(CriteriaType.BooleanMust, New BooleanClause(New BooleanQuery, Occur.MUST))
            End If

            If Me.EnableFreeLuceneQuery Then
                toReturn.Add(CriteriaType.FreeQuery, New BooleanClause(Nothing, Occur.MUST))
            End If

            Return toReturn

        End Function

        Private Function GetCurrentSearchQuery() As BooleanQuery
            Dim toreturn As New BooleanQuery()
            For Each row As GridViewRow In Me.grdSearchCriteria.Rows
                Dim clause As BooleanClause = CreateSearchClause(row)
                If Not clause Is Nothing Then
                    toreturn.Add(clause)
                End If
            Next
            If toreturn.GetClauses.Length = 0 Then
                Return Nothing
            End If
            Return toreturn
        End Function

        'Private Function GetGridViewSearchQuery() As Query
        '    Dim toreturn As New BooleanQuery()
        '    For Each row As GridViewRow In Me.grdSearchCriteria.Rows
        '        Dim clause As BooleanClause = CreateSearchClause(row)
        '        If Not clause Is Nothing Then
        '            toreturn.Add(clause)
        '        End If
        '    Next
        '    If toreturn.GetClauses.Length = 0 Then
        '        Return Nothing
        '    End If
        '    Return toreturn
        'End Function

        Public Enum CriteriaType
            PhraseQuery
            BooleanMust
            BooleanShould
            FreeQuery
        End Enum

        Private Function CreateSearchClause(ByVal objRow As GridViewRow) As BooleanClause
            Dim ctlOccur As OccurSelector = DirectCast(objRow.FindControl("ctlOccur"), OccurSelector)
            Dim ctlCriteriaType As HiddenField = DirectCast(objRow.FindControl("ctlCriteriaType"), HiddenField)
            Dim txtSearchCriteria As TextBox = DirectCast(objRow.FindControl("txtSearchCriteria"), TextBox)
            Dim _
                ctlSearchFieldSelector As SearchFieldSelector = _
                    DirectCast(objRow.FindControl("ctlSearchFieldSelector"), SearchFieldSelector)
            Dim ctlFuzzyFilter As FuzzyFilter = DirectCast(objRow.FindControl("ctlFuzzyFilter"), FuzzyFilter)
            Dim ctlSlopFilter As SlopFilter = DirectCast(objRow.FindControl("ctlSlopFilter"), SlopFilter)
            If String.IsNullOrEmpty(txtSearchCriteria.Text) Then
                Return Nothing
            End If
            Dim criterias As String = txtSearchCriteria.Text.Trim
            Dim terms As List(Of String) = GetListTerms(criterias)

            Dim occur As Occur = ctlOccur.SelectedObjectG
            Dim fieldDef As FieldDefinition = ctlSearchFieldSelector.SelectedObjectG
            Dim fieldName As String = ""
            If fieldDef IsNot Nothing Then
                fieldName = fieldDef.Id
            End If

            Dim objQuery As Query
            Select Case ctlCriteriaType.Value
                Case CriteriaType.PhraseQuery.ToString
                    objQuery = New PhraseQuery()
                    For Each strTerm As String In terms
                        DirectCast(objQuery, PhraseQuery).Add(New Term(fieldName, strTerm))
                    Next
                    If ctlSlopFilter.SlopRatio <> 0 Then
                        DirectCast(objQuery, PhraseQuery).Slop = ctlSlopFilter.SlopRatio
                    End If
                Case CriteriaType.BooleanMust.ToString, CriteriaType.BooleanShould.ToString
                    Dim fuzzyValue As Single = 1
                    If EnableCustomFuzzyCriteria Then
                        fuzzyValue = CSng(ctlFuzzyFilter.FuzzyRatio)
                    Else
                        fuzzyValue = CSng(DefaultFuzzyValue)
                    End If

                    Dim objTermOccur As Occur = occur.SHOULD
                    If ctlCriteriaType.Value = CriteriaType.BooleanMust.ToString Then
                        objTermOccur = Occur.MUST
                    End If
                    objQuery = New BooleanQuery()
                    For Each strTerm As String In terms
                        Dim objTerm As New Term(fieldName, strTerm)
                        Dim termQuery As Query
                        If fuzzyValue <> 1 Then
                            termQuery = New FuzzyQuery(objTerm, fuzzyValue)
                        Else
                            termQuery = New TermQuery(objTerm)
                        End If
                        DirectCast(objQuery, BooleanQuery).Add(termQuery, objTermOccur)
                    Next
                Case Else
                    objQuery = New TermQuery(New Term("", criterias))

            End Select

            Return New BooleanClause(objQuery, occur)

        End Function

        'Private Function BuildQuery(ByVal criterias As String, ByVal fieldName As String, ByVal type As CriteriaType, _
        '                             ByVal ratio As Single) As Query

        '    Dim terms As New List(Of String)(criterias.Split(" "c))
        '    Dim objQuery As Query
        '    Select Case type
        '        Case CriteriaType.PhraseQuery
        '            objQuery = New PhraseQuery()
        '            For Each strTerm As String In terms
        '                DirectCast(objQuery, PhraseQuery).Add(New Term(fieldName, strTerm))
        '            Next
        '            If ratio <> 0 Then
        '                DirectCast(objQuery, PhraseQuery).SetSlop(CInt(ratio))
        '            End If
        '        Case CriteriaType.BooleanMust, CriteriaType.BooleanShould
        '            Dim objTermOccur As BooleanClause.Occur = Occur.SHOULD
        '            If type = CriteriaType.BooleanMust Then
        '                objTermOccur = Occur.MUST
        '            End If
        '            objQuery = New BooleanQuery()
        '            For Each strTerm As String In terms
        '                Dim objTerm As New Term(fieldName, strTerm)
        '                Dim termQuery As Query
        '                If ratio <> 1 Then
        '                    termQuery = New FuzzyQuery(objTerm, ratio)
        '                Else
        '                    termQuery = New TermQuery(objTerm)
        '                End If
        '                DirectCast(objQuery, BooleanQuery).Add(termQuery, objTermOccur)
        '            Next
        '        Case Else
        '            objQuery = New TermQuery(New Term("", criterias))

        '    End Select
        '    Return objQuery

        'End Function

        Private Function GetListTerms(ByVal criterias As String) As List(Of String)
            Dim toReturn As New List(Of String)

            Dim listTerms As New List(Of String)(criterias.Split(" "c))

            If listTerms IsNot Nothing AndAlso listTerms.Count > 0 Then
                For Each term As String In listTerms
                    If term.Trim <> String.Empty Then
                        toReturn.Add(term.Trim)
                    End If
                Next
            End If

            Return toReturn
        End Function
#End Region
    End Class
End Namespace